from django import http
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render

# Create your views here.

from django.views import View

from django.http import JsonResponse
from apps.users.models import User
from apps.verifications.views import logger


class UsernameCountView(View):
    """判断用户名是否重复注册"""

    def get(self, request, username):
        """
        :param request: 请求对象
        :param username: 用户名
        :return: JSON
        """
        from apps.users.models import User
        count = User.objects.filter(username=username).count()
        return JsonResponse({'code': 0, 'errmsg': 'OK', 'count': count})


class MobileCountView(View):
    """判断手机号是否重复注册"""

    def get(self, request, mobile):
        """
        :param request: 请求对象
        :param mobile: 手机号
        :return: JSON
        """
        count = User.objects.filter(mobile=mobile).count()
        return JsonResponse({'code': 0, 'errmsg': 'OK', 'count': count})


############用户注册##################


class RegisterView(View):

    def post(self, request):
        import json
        # 1.接收参数：请求体中的JSON数据 request.body
        json_bytes = request.body
        json_str = json_bytes.decode()
        json_dict = json.loads(json_str)
        # json_dict = json.loads(request.body.decode())

        # 2.获取数据
        username = json_dict.get('username')
        password = json_dict.get('password')
        password2 = json_dict.get('password2')
        mobile = json_dict.get('mobile')
        allow = json_dict.get('allow')
        sms_code = json_dict.get('sms_code')
        # 3.验证数据

        from django import http
        import re
        from apps.users.models import User
        # 用户名　密码　确认密码　手机号　是否同意协议都要有
        if not all([username, password, password2, mobile, allow]):
            return JsonResponse({'code': 400, 'errmsg': '参数不全'})

        # 用户名满足规则　用户名不能重复
        if not re.match(r'^[a-zA-Z0-9_]{5,20}$', username):
            return JsonResponse({'code': 0, 'errmsg': '用户名不满足规则'})

        # 密码满足规则　确认密码和密码一致
        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return JsonResponse({'code': 400, 'errmsg': 'password格式有误!'})
        # 判断两次密码是否一致

        if password != password2:
            return JsonResponse({'code': 400, 'errmsg': '两次输入不对!'})
        # 手机号满足规则　手机号也不能重复
        # 判断手机号是否合法
        if not re.match(r'^1\d{10}$', mobile):
            return JsonResponse({'code': 400, 'errmsg': 'mobile格式有误!'})
        # 是否需要同意协议
        if allow != True:
            return JsonResponse({'code': 400, 'errmsg': 'allow格式有误!'})

        try:
            user = User.objects.create_user(username=username,
                                            password=password,
                                            mobile=mobile)
        except Exception as e:
            return http.JsonResponse({'code': 400, 'errmsg': '注册失败!'})

        # 4.数据入库
        # 方法一
        # user = User(username=username,password=password,mobile=mobile)
        # user.save()

        # 方法二
        # User.objects.create(username=username,password=password,mobile=mobile)

        # 密码加密
        # user=User.objects.create_user(username=username,password=password,mobile=mobile)

        # 如何设置session信息
        # request.session['user_id']=user.id

        # 系统django为我们提供了状态保持方法
        from django.contrib.auth import login
        # request, user
        # request 状态保持－－登录用户的状态保持
        # user　已经登陆的用户信息

        # 实现状态保持
        login(request, user)

        # 响应注册结果
        return http.JsonResponse({'code': 0, 'errmsg': '注册成功!'})


################################状态保持#################################################
# 如果需求是注册成功后即表示用户认证通过，那么此时可以在注册成功后实现状态保持（注册成功即登录）
# 如果需求是注册成功后不表示用户认证通过，那么此时不用在注册成功后实现状态保持。（注册成功后单独登录）

# 实现状态保持主要有两种方式
#     在客户端存储信息使用cookie
#     在服务器端存储信息使用session


######################用户登录#####################
'''
前端
    
    当用户把用户名和密码输入完成后，会点击登录按钮，这个时候前端应该发送一个axios请求

后端

    请求：接收数据，验证数据
    业务逻辑：验证用户名和密码是否正确，session
    响应：返回JSON数据　０成功　４００失败
    POST /login/
    
    
步骤：

    １．接收数据
    ２．验证数据
    ３．验证用户名和密码是否正确
    ４．session 
    ５．判断是否记住登录
    ６．返回响应
    
    
'''

import json



class LoginView(View):

    def post(self, request):

        # １．接收数据

        data = json.loads(request.body.decode())
        username = data.get('username')
        password = data.get('password')
        remembered = data.get('remembered')

        # ２．验证数据

        if not all({username, password}):
            return JsonResponse({'code': 400, 'errmsg': '用户名和密码不匹配'})

        import re
        from apps.users.models import User
        if re.match('^1[3-9]\d{9}$', username):
            # 手机号
            User.USERNAME_FIELD = 'mobile'
        else:
            # account 是用户名
            # 根据用户名从数据库获取 user 对象返回.
            User.USERNAME_FIELD = 'username'

        # ３．验证用户名和密码是否正确

        # 我们可以通过模型根据用户名来查询
        # 方法一：
        # User.objects.get(username=username)

        # 确认是手机号登录还是用户名登录

        # 方法二：
        from django.contrib.auth import authenticate
        # authenticate 传递用户名和密码，如果用户名正确则返回User信息，如果用户名和密码不正确则返回None
        User = authenticate(username=username, password=password)
        if User is None:
            return JsonResponse({'code': 400, 'errmsg': '账号或密码错误'})

        # ４．session

        # 进行状态保持
        from django.contrib.auth import login
        login(request, User)

        # ５．判断是否记住登录
        if remembered is not None:
            # 记住密码（两周或一个月），产品说了算
            request.session.set_expiry(None)

        else:
            # 不记住密码，浏览器关闭session过期
            request.session.set_expiry(0)

        # ６．返回响应
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        # 为了首页显示用户信息
        response.set_cookie('username', username)

        # 必须是在登陆之后　合并
        from apps.carts.utils import merge_cookie_to_redis
        response = merge_cookie_to_redis(request, response)
        return response


#####################退出登录#######################################
'''

前端
    
    当用户点击退出按钮的时候，前端发送一个axios 
    delete请求


后端

    请求
    业务逻辑：退出
    响应　返回JSON数据
       
'''

from django.contrib.auth import logout


class LogoutView(View):

    def delete(self, request):
        # 删除session信息
        logout(request)
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        # 删除cookie信息
        response.delete_cookie('username')
        return response


#####################判断用户是否登录###############
'''
用户中心
必须是登录用户

问题
LoginRequiredMixin未登录用户会返回重定向，重定向并不是JSON数据
我们需要返回JSON数据

'''

from utils.views import LoginRequiredJSONMixin


class CenterView(LoginRequiredJSONMixin, View):

    def get(self, requset):
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


#################用户基本信息###############
# 用户中心　　用户中心必须是登录用户
#
# LoginRequiredMixin未登录用户，会返回重定向　重定向并不是JSON数据
# 我们需要返回JSON数据


from utils.views import LoginRequiredJSONMixin


class CeaterView(LoginRequiredJSONMixin, View):

    def get(self, request):
        info_data = {
            'username': request.user.username,
            'email': request.user.email,
            'mobile': request.user.mobile,
            'email_active': request.user.email_active
        }

        return JsonResponse({'code': 0, 'errmsg': 'ok', 'info_data': info_data})


################保存邮件###################
'''
需求：
    １．保存邮件地址
    ２．发送一封激活邮件
    ３．用户激活邮件

前端：
    当用户输入邮件之后，点击保存，这个时候会发送一个axios请求

后端：
    请求：　接受请求，获取数据
    业务逻辑：保存邮箱地址，发送激活邮件
    响应：　JSON　code=0
    路由：PUT
    步骤：
        １．接收数据
        ２．获取数据
        ３．保存邮箱地址
        ４．发送一封激活邮件
        ５．返回响应
'''

import re
import json


class EmailView(LoginRequiredJSONMixin, View):
    def put(self, request):
        # １．接收数据
        data = json.loads(request.body.decode())

        # ２．获取数据
        email = data.get('email')

        # 验证数据
        if not email:
            return JsonResponse({'code': 400,
                                 'errmsg': '缺少email参数'})

        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return JsonResponse({'code': 400,
                                 'errmsg': '参数email有误'})

        # # 赋值email字段
        # try:
        #     request.user.email = email
        #     request.user.save()
        # except Exception as e:
        #     logging.error(e)
        #     return JsonResponse({'code': 400, 'errmsg': '添加邮箱失败'})
        #
        # # 响应添加邮箱结果
        # return JsonResponse({'code': 0, 'errmsg': '添加邮箱成功'})

        # ３．保存邮箱地址
        user = request.user
        user.email = email
        user.save()

        # ４．发送一封激活邮件
        # 设置１６３邮箱服务器，相当于我们开启了让１６３帮助我们发送邮件，同时设置了一些信息（特别是授权码）
        from django.core.mail import send_mail
        # subject　主题　
        subject = '美多商城激活码'
        # message　邮件内容　如果是网页邮件的话用　html_message
        message = ''
        # from_email 发件人　
        from_email = 'gao_lian_xiao@126.com'
        # recipient_list　收件人列表
        recipient_list = ['gao_lian_xiao@163.com']

        # 4.2组织我们的激活邮件
        from apps.users.utils import generic_email_verify_token
        token = generic_email_verify_token(request.user.id)
        verify_url = "http://www.meiduo.site:8080/success_verify_email.html?token=%s" % token

        # html_message = "点击按钮进行激活<a href='https://translate.google.cn/?token=%s'>激活</a>"%token
        html_message = '<p>尊敬的用户您好！</p>' \
                       '<p>感谢您使用美多商城。</p>' \
                       '<p>您的邮箱为：%s 。请点击此链接激活您的邮箱：</p>' \
                       '<p><a href="%s">%s<a></p>' % (email, verify_url, verify_url)
        # send_mail(subject=subject,
        #           message=message,
        #           from_email=from_email,
        #           recipient_list=recipient_list,
        #           html_message=html_message)
        from celery_tasks.email.tasks import celery_send_email
        celery_send_email.delay(
            subject=subject,
            message=message,
            from_email=from_email,
            recipient_list=recipient_list,
            html_message=html_message
        )
        # ５．返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


###########调用send_mail方法###################
'''
需求：　激活用户的邮件

前端：　用户点击激活链接，激活链接携带了token

后端：　

    请求：　接收参数，获取参数，验证参数

    业务逻辑：　used_id ,根据用户的id查询数据，修改数据

    响应：　返回响应JSON

    路由：　PUT emails/verification/  说明：token并没有在body里面

    步骤：

        １．就收请求
        ２．获取参数
        ３．验证参数
        ４．获取user_id
        ５．根据user_id查询数据
        ６．修改数据
        ７．返回响应
'''


class EmailVerifyView(View):

    def put(self, request):

        # １．就收请求
        params = request.GET

        # ２．获取参数
        token = params.get('token')

        # ３．验证参数
        if token is None:
            return JsonResponse({'code': 400, 'errmsg': '参数缺失'})

        # ４．获取user_id
        from apps.users.utils import check_verify_token
        user_id = check_verify_token(token)
        if user_id is None:
            return JsonResponse({'code': 400, 'errmsg': '参数错误'})

        # ５．根据user_id查询数据
        user = User.objects.get(id=user_id)
        # ６．修改数据
        user.email_active = True
        user.save()
        # ７．返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


###############地址的增删改查##############
'''
请求
业务逻辑：　数据库的增删改查
响应


增加
    １．接收数据
    ２．验证数据
    ３．数据入库
    ４．返回响应

删除
    １．查询指定记录
    ２．删除数据（物理删除，逻辑删除）
    ３．返回响应

修改
    １．查询指定的记录
    ２．接收数据
    ３．验证数据
    ４．数据更新
    ５．返回响应

查询
    １．查询指定数据
    ２．将对象数据转换为字典数据
    ３．返回响应
'''

'''
需求：　新增地址
    
    前端：　当用户填写完成地址信息后，前端应该发送一个axios请求，会携带相关信息（POST---body）

后端：
    请求：　接收数据，获取参数，验证参数
    业务逻辑：　数据入库
    响应：　返回响应
    路由：　POST　/addresses/create/
    步骤：　
        １．接收数据
        ２．获取参数，验证参数
        ３．数据入库
        ４．返回响应
'''

from apps.users.models import Address



class AddressCreateView(LoginRequiredJSONMixin, View):

    def post(self, request):

        # １．接收数据
        data = json.loads(request.body.decode())

        # ２．获取参数，验证参数
        receiver = data.get('receiver')
        province_id = data.get('province_id')
        city_id = data.get('city_id')
        district_id = data.get('district_id')
        place = data.get('place')
        mobile = data.get('mobile')
        tel = data.get('tel')
        email = data.get('email')
        user = request.user

        # 验证参数
        # 验证必传参数
        # 省市区的id是否正确
        # 详细地址的长度
        # 手机号
        # 固定电话
        # 邮箱
        # if not all([receiver, province_id, city_id, district_id, place, mobile]):
        #     return http.HttpResponseBadRequest('缺少必传参数')
        # if not re.match(r'^1[3-9]\d{9}$', mobile):
        #     return http.HttpResponseBadRequest('参数mobile有误')
        # if tel:
        #     if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
        #         return http.HttpResponseBadRequest('参数tel有误')
        # if email:
        #     if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
        #         return http.HttpResponseBadRequest('参数email有误')

        # ３．数据入库
        address = Address.objects.create(
            user=user,
            title=receiver,
            receiver=receiver,
            province_id=province_id,
            city_id=city_id,
            district_id=district_id,
            place=place,
            mobile=mobile,
            tel=tel,
            email=email
        )

        address_dict = {


            'id': address.id,
            'title': address.title,
            'receiver': address.receiver,
            'province': address.province.name,
            'city': address.city.name,
            'district': address.district.name,
            'place': address.place,
            'mobile': address.mobile,
            'tel': address.tel,
            'email': address.email
        }

        # ４．返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'address': address_dict})




class AddressView(View):

    def get(self,request):
        # １．查询指定数据
        user = request.user
        # addresses = user.addresses
        addresses = Address.objects.filter(user=user,is_deleted=False)

        # ２．将对象数据转换为字典数据
        address_list = []
        for address in addresses:
            address_list.append({
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
            })

        # ３．返回响应
        return JsonResponse({'code':0,'errmsg':'ok','addresses':address_list})



class UpdateDestroyAddressView(LoginRequiredJSONMixin, View):
    """修改和删除地址"""

    # 修改地址
    def put(self, request, address_id):

        # 接收参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.JsonResponse({'code': 400,
                                      'errmsg': '缺少必传参数'})


        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({'code': 400,
                                      'errmsg': '参数mobile有误'})

        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.JsonResponse({'code': 400,
                                          'errmsg': '参数tel有误'})
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.JsonResponse({'code': 400,
                                          'errmsg': '参数email有误'})

        # 判断地址是否存在,并更新地址信息
        try:
            Address.objects.filter(id=address_id).update(
                user = request.user,
                title = receiver,
                receiver = receiver,
                province_id = province_id,
                city_id = city_id,
                district_id = district_id,
                place = place,
                mobile = mobile,
                tel = tel,
                email = email
            )
        except Exception as e:
            from apps.verifications.views import logger
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '更新地址失败'})

        # 构造响应数据
        address = Address.objects.get(id=address_id)
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        # 响应更新地址结果
        return JsonResponse({'code': 0, 'errmsg': '更新地址成功', 'address': address_dict})



        # 删除地址
    def delete(self, request, address_id):

        try:
            # 查询要删除的地址
            address = Address.objects.get(id=address_id)

            # 将地址逻辑删除设置为True
            address.is_deleted = True
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '删除地址失败'})

        # 响应删除地址结果
        return http.JsonResponse({'code': 0, 'errmsg': '删除地址成功'})



class DefaultAddressView(LoginRequiredJSONMixin, View):
    """设置默认地址"""

    def put(self, request, address_id):
        """设置默认地址"""
        try:
            # 接收参数,查询地址
            address = Address.objects.get(id=address_id)

            # 设置地址为默认地址
            request.user.default_address = address
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '设置默认地址失败'})

        # 响应设置默认地址结果
        return JsonResponse({'code': 0, 'errmsg': '设置默认地址成功'})



class UpdateTitleAddressView(LoginRequiredJSONMixin, View):
    """设置地址标题"""

    def put(self, request, address_id):
        """设置地址标题"""
        # 接收参数：地址标题
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')

        try:
            # 查询地址
            address = Address.objects.get(id=address_id)

            # 设置新的地址标题
            address.title = title
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '设置地址标题失败'})

        # 4.响应删除地址结果
        return JsonResponse({'code': 0, 'errmsg': '设置地址标题成功'})



class ChangePasswordView(LoginRequiredMixin, View):
    # 修改密码

  def put(self, request):
        """实现修改密码逻辑"""
        # 接收参数
        dict = json.loads(request.body.decode())
        old_password = dict.get('old_password')
        new_password = dict.get('new_password')
        new_password2 = dict.get('new_password2')

        # 校验参数
        if not all([old_password, new_password, new_password2]):
           return http.JsonResponse({'code':400,
                                     'errmsg':'缺少必传参数'})


        result = request.user.check_password(old_password)
        if not result:
            return http.JsonResponse({'code':400,
                                      'errmsg':'原始密码不正确'})

        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_password):
            return http.JsonResponse({'code':400,
                                      'errmsg':'密码最少8位,最长20位'})

        if new_password != new_password2:
            return http.JsonResponse({'code':400,
                                      'errmsg':'两次输入密码不一致'})

        # 修改密码
        try:
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:

            return http.JsonResponse({'code':400,
                                      'errmsg':'修改密码失败'})

        # 清理状态保持信息
        logout(request)

        response = http.JsonResponse({'code':0,
                                      'errmsg':'ok'})

        response.delete_cookie('username')

        # # 响应密码修改结果：重定向到登录界面
        return response




####################用户浏览记录#################################
'''
一：根据页面效果，分析需求

１．最近浏览记录只有登录用户才可以访问，我们只记录用户的浏览记录
２．浏览记录应该有顺序
３．没有分页

二：功能

在用户访问商品详情的时候，添加浏览记录，在个人中心展示浏览记录

三：分析

问题１：保存那些数据？
用户id，商品id，顺序（访问时间）

问题２：保存在哪？
一般保存在数据库（缺点：慢　频繁操作数据库）　最好保存在redis中

user_id sku_id 顺序

'''

'''
添加浏览记录
    
    前端：当登录用户访问某一个具体SKU页面的时候，发送一个axios请求，请求携带sku_id
    
    后端：
        
        请求：接收请求，获取请求参数，验证参数
        业务逻辑：　连接redis，先去重，然后保存到redis中，只保存５条记录
        响应：　返回JSON
        路由：　POST　browse_histories
        步骤：
            １．接收请求
            ２．获取参数
            ３．验证参数
            ４．连接redis
            ５．去重
            ６．保存到redis
            ７．只保存５条记录
            ８．返回JSON
            
           
            
展示浏览记录

前端：用户访问浏览记录的时候，发送一个axios请求，请求会携带session信息

后端：
    请求：
    业务逻辑：　连接redis，获取redis数据([1,2,3]),根据商品id进行数据查询，将对象转换为字典
    响应：　ＧＥＴ
    步骤：
        １．连接redis
        2.获取redis数据([1,2,3])
        ３．根据商品id进行数据查询
        ４．将对象转换为字典
        ５．返回响应
'''




from apps.goods.models import SKU
from django_redis import get_redis_connection


class UserHistoryView(View):

    def post(self,request):

        user = request.user

        # １．接收请求
        data = json.loads(request.body.decode())

        # ２．获取参数
        sku_id = data.get('sku_id')

        # ３．验证参数
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return ({'code':400,'errmsg':'没有此商品'})

        # ４．连接redis
        redis_cli = get_redis_connection('history')

        # ５．去重
        redis_cli.lrem('history_%s'%user.id,0,sku_id)

        # ６．保存到redis
        redis_cli.lpush('history_%s'%user.id,sku_id)

        # ７．只保存５条记录
        redis_cli.ltrim('history%s'%user.id,0,4)

        # ８．返回JSON
        return JsonResponse({'code':0,'errmsg':'ok'})



    def get(self,request):

        # １．连接redis
        redis_cli = get_redis_connection('history')

        # 2.获取redis数据([1, 2, 3])
        ids = redis_cli.lrange('history_%s'%request.user.id,0,4)

        # ３．根据商品id进行数据查询
        history_list = []
        for sku_id in ids:
            sku = SKU.objects.get(id=sku_id)

        # ４．将对象转换为字典
            history_list.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })

        # ５．返回响应
        return JsonResponse({'code':0,'errmsg':'ok','skus':history_list})